import {Material} from './Material.js';
import {cloneUniforms} from '../renderers/shaders/UniformsUtils.js';

import default_vertex from '../renderers/shaders/ShaderChunk/default_vertex.glsl.js';
import default_fragment from '../renderers/shaders/ShaderChunk/default_fragment.glsl.js';

/**
 * parameters = {
 *  defines: { "label" : "value" },
 *  uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } },
 *
 *  fragmentShader: <string>,
 *  vertexShader: <string>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 *
 *  lights: <bool>,
 *
 *  skinning: <bool>,
 *  morphTargets: <bool>,
 *  morphNormals: <bool>
 * }
 */

function ShaderMaterial(parameters) {

  Material.call(this);

  this.type = 'ShaderMaterial';

  this.defines = {};
  this.uniforms = {};

  this.vertexShader = default_vertex;
  this.fragmentShader = default_fragment;

  this.linewidth = 1;

  this.wireframe = false;
  this.wireframeLinewidth = 1;

  this.fog = false; // set to use scene fog
  this.lights = false; // set to use scene lights
  this.clipping = false; // set to use user-defined clipping planes

  this.skinning = false; // set to use skinning attribute streams
  this.morphTargets = false; // set to use morph targets
  this.morphNormals = false; // set to use morph normals

  this.extensions = {
    derivatives: false, // set to use derivatives
    fragDepth: false, // set to use fragment depth values
    drawBuffers: false, // set to use draw buffers
    shaderTextureLOD: false // set to use shader texture LOD
  };

  // When rendered geometry doesn't include these attributes but the material does,
  // use these default values in WebGL. This avoids errors when buffer data is missing.
  this.defaultAttributeValues = {
    'color': [1, 1, 1],
    'uv': [0, 0],
    'uv2': [0, 0]
  };

  this.index0AttributeName = undefined;
  this.uniformsNeedUpdate = false;

  this.glslVersion = null;

  if (parameters !== undefined) {

    if (parameters.attributes !== undefined) {

      console.error('THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.');

    }

    this.setValues(parameters);

  }

}

ShaderMaterial.prototype = Object.create(Material.prototype);
ShaderMaterial.prototype.constructor = ShaderMaterial;

ShaderMaterial.prototype.isShaderMaterial = true;

ShaderMaterial.prototype.copy = function (source) {

  Material.prototype.copy.call(this, source);

  this.fragmentShader = source.fragmentShader;
  this.vertexShader = source.vertexShader;

  this.uniforms = cloneUniforms(source.uniforms);

  this.defines = Object.assign({}, source.defines);

  this.wireframe = source.wireframe;
  this.wireframeLinewidth = source.wireframeLinewidth;

  this.lights = source.lights;
  this.clipping = source.clipping;

  this.skinning = source.skinning;

  this.morphTargets = source.morphTargets;
  this.morphNormals = source.morphNormals;

  this.extensions = Object.assign({}, source.extensions);

  this.glslVersion = source.glslVersion;

  return this;

};

ShaderMaterial.prototype.toJSON = function (meta) {

  const data = Material.prototype.toJSON.call(this, meta);

  data.glslVersion = this.glslVersion;
  data.uniforms = {};

  for (const name in this.uniforms) {

    const uniform = this.uniforms[name];
    const value = uniform.value;

    if (value && value.isTexture) {

      data.uniforms[name] = {
        type: 't',
        value: value.toJSON(meta).uuid
      };

    } else if (value && value.isColor) {

      data.uniforms[name] = {
        type: 'c',
        value: value.getHex()
      };

    } else if (value && value.isVector2) {

      data.uniforms[name] = {
        type: 'v2',
        value: value.toArray()
      };

    } else if (value && value.isVector3) {

      data.uniforms[name] = {
        type: 'v3',
        value: value.toArray()
      };

    } else if (value && value.isVector4) {

      data.uniforms[name] = {
        type: 'v4',
        value: value.toArray()
      };

    } else if (value && value.isMatrix3) {

      data.uniforms[name] = {
        type: 'm3',
        value: value.toArray()
      };

    } else if (value && value.isMatrix4) {

      data.uniforms[name] = {
        type: 'm4',
        value: value.toArray()
      };

    } else {

      data.uniforms[name] = {
        value: value
      };

      // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far

    }

  }

  if (Object.keys(this.defines).length > 0) data.defines = this.defines;

  data.vertexShader = this.vertexShader;
  data.fragmentShader = this.fragmentShader;

  const extensions = {};

  for (const key in this.extensions) {

    if (this.extensions[key] === true) extensions[key] = true;

  }

  if (Object.keys(extensions).length > 0) data.extensions = extensions;

  return data;

};


export {ShaderMaterial};
